package graph_viewer.util;

import java.awt.Graphics;
import java.awt.FontMetrics;
import java.awt.Font;
import java.util.ArrayList;
import java.util.Iterator;

import graph_viewer.app_exception.*;
import graph_viewer.canvas.*;
import graph_viewer.drawer.*;
import graph_viewer.structure.*;
import graph_viewer.layout.*;
import graph_viewer.util.*;

public class Node_Position_Helper{
	static public void set_cluster_node_position( Node a_cluster, Graph a_graph ){
		Debug.println( "\tTo set_cluster_node_position: " + a_cluster, 33 );

		one_level_set_cluster_node_position( a_cluster );

		Debug.println( "\tFinsh set_cluster_node_position: " + a_cluster, 33 );
		
		ArrayList ancestors	 = a_graph.find_tree().get_ancesters( a_cluster );
		for ( Iterator iter = ancestors.iterator(); iter.hasNext(); ){
			Node an_ance	 = (Node) iter.next();

			one_level_set_cluster_node_position( an_ance ); 
			set_root_position( a_cluster, a_graph );
		}
	}
	
	static public void one_level_set_cluster_node_position( Node a_cluster ){
		if ( a_cluster.get_is_closed() ){
			return;
		}

		Debug.println( "\t TO do one_level_set_cluster_node_position for " + a_cluster, 33 );

		ArrayList children	= a_cluster.get_children();
		
		int 
			upper = Integer.MAX_VALUE,
			bottom	= Integer.MIN_VALUE,
			left	= Integer.MAX_VALUE,
			right	= Integer.MIN_VALUE;

		for ( Iterator _i = children.iterator(); _i.hasNext(); ){
			//Debug.println( "upper " + upper, 3 );
			//Debug.println( "bottom " + bottom, 3 );
			//Debug.println( "left " + left, 3 );
			//Debug.println( "right " + right, 3 );

			Node a_child	= (Node)_i.next();			

			Debug.println( "\t After Child " + a_child.get_label() , 20 );
			int[] 
				location			= a_child.get_position().find_location(),
				right_bottom	= a_child.get_position(). find_right_bottom();

			int 
				child_left		= location[0],
				child_upper		= location[1],
				child_right		= right_bottom[0],
				child_bottom	= right_bottom[1];
			
			//Debug.println( "child_upper " + child_upper, 3 );
			//Debug.println( "child_bottom " + child_bottom, 3 );
			//Debug.println( "child_left " + child_left, 3 );
			//Debug.println( "child_right " + right, 3 );
			
			if ( child_left < left ){
				left	= child_left;
			}

			if ( child_upper < upper ){
				upper = child_upper;
			}

			if ( child_right > right ){
				right = child_right;
			}

			if ( child_bottom > bottom ){
				bottom = child_bottom;
			}
		}
		
		Debug.println( "\t Finish all Children "  , 18 );
		int margin	= 4;

		int
			cluster_left	=	left - margin,
			cluster_upper	=	upper - margin,
			cluster_right	=	right + margin,
			cluster_bottom=	bottom + margin;
		int 
			cluster_width		= cluster_right - cluster_left,
			cluster_heigth		= cluster_bottom - cluster_upper,
			cluster_center_x	= cluster_width/2 + cluster_left,
			cluster_center_y	= cluster_heigth/2 + cluster_upper;
		
		int 
			label_height	= 18;
		Node_Position	np	 = new Node_Position( cluster_center_x, cluster_center_y, cluster_width, cluster_heigth + label_height);
		
		a_cluster.set_position( np );

	
		Debug.println( "\tFinish do one_level_set_cluster_node_position for " + a_cluster, 33 );

	}

	static public void one_level_set_cluster_node_position( Node a_cluster, Drawer_Canvas graph_canvas ){
		one_level_set_cluster_node_position( a_cluster );
	}
	
	public static void set_psuedo_node_size( Node a_node ){	
		if (! a_node.get_is_psuedo() ){
			throw new App_Exception( "Try to set_psuedo_node_size to a NON psuedo node!" );
		}

		set_default_size( a_node );
	}
	
	public static void set_leaf_cluster_size( Node a_node, Graphics g ){	
		if (! a_node.is_leaf_cluster() ){
			throw new App_Exception( "Try to set leaf node size to a NON leaf node! node: " + a_node );
		}

		if ( a_node.get_is_psuedo() ){
			set_psuedo_node_size( a_node );
			return;
		}
			
		g.setFont( Drawer.FONT );
		FontMetrics font_metrics = g.getFontMetrics();
		
		Node_Position np = a_node.get_position();
		if ( np == null ){
			np = new Node_Position();
		}

		int margin	= Layout.NODE_LABEL_MARGIN;

		int 		
			standard_height	= font_metrics.getHeight(),// + 2 * margin,
			standard_width	= font_metrics.stringWidth( Drawer_Util.get_displayed_label(a_node) )  + 2 * margin;

		np.set_width( standard_width  );
		np.set_height( standard_height );
		
		a_node.set_position( np );
		
		//for static removing overlaps
		//set_default_size( a_node );
	}

	public static void set_default_size( Node node ){	
		node.get_position().set_width( Layout.DEFAULT_NODE_WIDTH );
		node.get_position().set_height( Layout.DEFAULT_NODE_HEIGHT );
	}

	public static void set_min_size( Node node ){		
		node.get_position().set_width( Layout.MINI_NODE_WIDTH );
		node.get_position().set_height( Layout.MINI_NODE_HEIGHT );
	}

	//create a randomly located node position
	public static  Node_Position	poistion_randomizer(){
		Node_Position	screen_boundary	= GUI_Util.find_screen_boundary();

		int[]	screen_location	= screen_boundary.find_location();
		int	
			screen_width	= screen_boundary.get_width() -  2 * Layout.NODE_MARGIN,
			screen_height	= screen_boundary.get_height() - 2 * Layout.NODE_MARGIN;
		
		double
			x_randomizer	= screen_location[0] + Layout.NODE_MARGIN + Math.random() * screen_width,
			y_randomizer	= screen_location[1] + Layout.NODE_MARGIN + Math.random() * screen_height;

		double
			width	= 50 +  Math.random() * Structure_Util.find_default_node_width(), 
			height	= 20 + Math.random() * Structure_Util.find_default_node_height();

		return new Node_Position( (int)x_randomizer,(int)y_randomizer, (int)width, (int)height );
		//return new Node_Position( (int)x_randomizer,(int)y_randomizer );
	}

	//set the cluster's children evenly around the screen border.
	public static void	set_opened_children_initial_position( Node a_cluster, Drawer_Canvas graph_canvas ){	
		if ( graph_canvas.get_is_positions_set() ){
			return ;
		}
		Node_Position	screen_boundary	= GUI_Util.find_screen_boundary();

		Debug.println( "\tset_opened_children_initial_position of " + a_cluster, 48 );
		Graph a_graph	= graph_canvas.get_graph();
		ArrayList	 children		= a_cluster.get_children();
		int	 children_size		= children.size();
		
		Debug.println( "\twith children: " + children, 17 );
	
		int[]	screen_location	= screen_boundary.find_location();
		
		int	
			width	= screen_boundary.get_width(),
			height	= screen_boundary.get_height();
		
		int nodes_number_in_an_edge	= children_size/4 + 1;
		int	 
			horizontal_interval	= width/(nodes_number_in_an_edge + 1),
			vertical_interval	= height/(nodes_number_in_an_edge + 1);
		
		for ( int i = 0; i < children_size; i++ ){
			Node child	= (Node)children.get( i );
			
			child.set_is_labelled( true );
			child.set_is_closed( true );

			Debug.println( "\tallocate position to " + child, 24 );	
			
			int _horizontal_width,  _vertical_height;

			int margin_btw_nodes	= 0;//20;

			if ( i < nodes_number_in_an_edge ){
				 _horizontal_width	= screen_location[0] + i * horizontal_interval;
				 _vertical_height		= screen_location[1];
			}else if ( nodes_number_in_an_edge <= i || i < (2 * nodes_number_in_an_edge) ){							
				 _horizontal_width	= screen_location[0] + ( i - nodes_number_in_an_edge) * ( horizontal_interval + margin_btw_nodes );
				 _vertical_height		= screen_location[1] + height;
			}else if ( 2 * nodes_number_in_an_edge <= i || i < (3 * nodes_number_in_an_edge) ){						
				 _horizontal_width	= screen_location[0] ;
				 _vertical_height		= screen_location[1] + (i - 2 * nodes_number_in_an_edge) * ( vertical_interval + margin_btw_nodes ) ;
			}else { // ( 3 * nodes_number_in_an_edge < i < 4 * nodes_number_in_an_edge )
				 _horizontal_width	= screen_location[0] + width;
				 _vertical_height		= screen_location[1] + (i - 2 * nodes_number_in_an_edge) *( vertical_interval + margin_btw_nodes ) ;
			}
			
			Node_Position	assigned_position	= new Node_Position( _horizontal_width, _vertical_height );
			child.set_position( assigned_position );
			
			//child.set_position( poistion_randomizer() );

			set_leaf_cluster_size( child, graph_canvas.getGraphics() );			
		}// end of for
		
		Debug.println( "\tfinish: set_opened_children_initial_position of " + a_cluster, 43 );	
		
		Debug.println( "\twith children: " + children, 43);
	}

	static public void initialize_root_position( Graph a_graph ){
		
		Node root	 = Structure_Util.find_graph_root( a_graph );

		if ( root.get_position() != null ){
			return;
		}

		int[] screen_center	= GUI_Util.find_screen_center();

		Node_Position screen_boundary	= GUI_Util.find_screen_boundary();

		Node_Position	 root_position	= new Node_Position( screen_boundary );
		
		root.set_position( root_position );
	}

	static private void set_root_position( Node root, Graph a_graph ){
		if (!root.is_root() ){
			return ;
		}
		
		Node_Position	root_initial_position	= root.get_position();
		Node_Position	new_root_position	= new Node_Position( root_initial_position );
		
		int[] screen_center	= GUI_Util.find_screen_center();
		new_root_position.set_x_center( screen_center[0] );
		new_root_position.set_y_center( screen_center[1] );

		root.set_position( new_root_position );
		Layout_Util.move_descendants( a_graph, root, root_initial_position );
	}

	static public void set_root_position( Graph a_graph ){
		Node root	 = Structure_Util.find_graph_root( a_graph );
		set_root_position( root, a_graph );
	}
	
	static public void constraint_root_to_screen_left_top( Graph a_graph ){		
		Node root	 = Structure_Util.find_graph_root( a_graph );

		Node_Position	 root_position	= root.get_position();

		int[] location	= root_position.find_location();
		int	x_location	= location[0],
			y_location	= location[1];

		for ( Iterator _i = a_graph.find_displayed_nodes().iterator(); _i.hasNext(); ){
			Node a_displayed_node	= (Node)_i.next();

			Node_Position	 np	= a_displayed_node.get_position();

			int 
				x_center	= np.get_x_center(),
				y_center	= np.get_y_center();
		
			int screen_margin	= 5;
			if ( x_location < 0 ){
				x_center -= (x_location - screen_margin ); // 
				np.set_x_center( x_center );
			}

			if ( y_location < 0 ){
				y_center	-= (y_location - screen_margin );
				np.set_y_center( y_center );
			}			
		}
		
	}

	static public  ArrayList clone_nodes_position( ArrayList clusters ){

		ArrayList clusters_positions_clone	= new ArrayList();

		for ( Iterator _i = clusters.iterator(); _i.hasNext(); ){
			Node child	= (Node)_i.next();
			Node_Position	a_child_positions_clone	= new Node_Position( child.get_position() );

			clusters_positions_clone.add( a_child_positions_clone );
		}

		return clusters_positions_clone;
	}
}//:~
